#! /usr/bin/env python
# -*- coding: utf-8 -*-

# *************************************************************
#     Filename @  ponitor.py
#       Author @  Huoty
#  Create date @  2016-07-08 16:09:03
#  Description @  process monitor
# *************************************************************

from __future__ import absolute_import, print_function, division

import os
import sys
from argparse import ArgumentParser

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
from pps import processes, mem_percent, cpu_percent, UnfoundException


error_mail_template = '''\
Logger Name:        %(name)s
Message type:       %(levelname)s
Location:           %(pathname)s:%(lineno)d
Module:             %(module)s
Function:           %(funcName)s
Host:               %(host)s
Time:               %(asctime)s

Message:

%(message)s
'''

def get_logger(name):
    import logging
    from logging.handlers import SMTPHandler

    class CustomSMTPHandler(SMTPHandler):
        def emit(self, record):
            # add extra record
            record.host = __import__("socket").gethostname()
            super(CustomSMTPHandler, self).emit(record)

        def getSubject(self, record):
            formatter = logging.Formatter(fmt=self.subject)
            return formatter.format(record)

    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    # add main log handler for error
    mail_handler = CustomSMTPHandler(
            mailhost = 'smtp.yeah.net',
            fromaddr = os.environ["MAIL_ADDR"],
            toaddrs  = ('loveqing2013@foxmail.com',),
            subject  = '%(name)s Error: %(message)s',
            credentials = (os.environ["MAIL_ADDR"], os.environ["MAIL_PASS"]))
    mail_handler.setLevel(logging.ERROR)
    mail_handler.setFormatter(logging.Formatter(error_mail_template))
    logger.addHandler(mail_handler)

    # add stream log handler for info
    stdout_handler = logging.StreamHandler(sys.stdout)
    stdout_handler.setLevel(logging.DEBUG)
    stdout_handler.setFormatter(logging.Formatter(
        '[%(levelname)s %(asctime)s] %(message)s'
    ))
    logger.addHandler(stdout_handler)

    return logger
    
log = get_logger("PonitorCron")

def monitor(mem_limit=60, cpu_limit=90, ignore=None, min_pid=0):
    """Watch all processes in the system

    If you exceed limit was killed
    """
    log.info("Start Scanning ...")
    ignore = ignore if ignore else []
    for p in processes():
        for pname in ignore:
            if pname in p.cmd:
                continue

        if p.pid < min_pid:
            continue

        try:
            p.update()
            total_mem_percent = mem_percent()
            total_cpu_percent = cpu_percent()

            condition1 = p.mem > mem_limit and total_mem_percent > 80
            condition2 = p.cpu > cpu_limit and total_cpu_percent > 90

            if condition1 or condition2:
                p.kill()
                log.info("kill: %s, mem: %s, cpu: %s" % (repr(p), p.mem, p.cpu))
        except UnfoundException as err:
            log.error(err)
        except Exception as err:
            log.exception(err)


# Script starts from here

if __name__ == "__main__":
    parser = ArgumentParser(prog="ponitor",
                        description="Watch all processes in the system",
                        epilog="Example: ponitor --ignore=atom,terminator")
    parser.add_argument("--mem-limit",
                        type=float,
                        default=60,
                        help="CPU limit")
    parser.add_argument("--cpu-limit",
                        type=float,
                        default=90,
                        help="Memory limit")
    parser.add_argument("--ignore",
                        type=str,
                        default=None,
                        help="Ignore some proces")
    parser.add_argument("--min-pid",
                        type=int,
                        default=0,
                        help="Lower limit of the process id")

    options = parser.parse_args()
    ignore_list = options.ignore.split(",") if options.ignore else []
    monitor(
        mem_limit=options.mem_limit, 
        cpu_limit=options.cpu_limit, 
        ignore=ignore_list,
        min_pid=options.min_pid
    )
    log.info("executed successfully")
    
